﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Vowei.Data;
using Vowei.Core.Models;
using Vowei.Web.Helpers;
using Newtonsoft.Json;
using Vowei.Data.Models;
using System.Runtime.Serialization.Json;
using System.IO;
using System.Web.Script.Serialization;

namespace vowei.test
{
    [TestClass]
    public class JsonTest : TestBase
    {
        public void SerializeTestHelper<T>() where T : INamedTable, new()
        {
            var item = new T() { Title = "SerializeTestHelper", ID = new Guid("11111111-1111-1111-1111-111111111111")};
            var str = ViewHelpers.SerializeToJson(item);
            Assert.IsTrue(!string.IsNullOrEmpty(str));

            // 因为在转换DateTime数据的时候，使用了JavaScriptDateTimeConverter
            // 看起来就没有办法再将结果Json字符串转换回原来的对象了
            //var revert = (T)JsonConvert.DeserializeObject(str, typeof(T));
            //Assert.AreEqual(item.ID, revert.ID);
            //Assert.AreEqual<string>(item.Title, revert.Title);
        }

        [TestMethod]
        public void SerializeInstanceTest()
        {
            SerializeTestHelper<Project>();
            SerializeTestHelper<Milestone>();
            SerializeTestHelper<Ticket>();
            SerializeTestHelper<Task>();
            SerializeTestHelper<Requirement>();
            SerializeTestHelper<Bug>();
        }

        public void SerializeDatabaseSingleInstanceTestHelper<T>(VoweiContext context)
            where T : class
        {
            var table = context.Resolve<T>();

            // TODO: 当Bug, MeetingRequest有默认数据以后，将下面的代码改成First();
            var item = table.Query.FirstOrDefault();

            var str = ViewHelpers.SerializeToJson(item);

            Assert.IsTrue(!string.IsNullOrEmpty(str));
        }

        [TestMethod]
        public void SerializeDatabaseSingleInstanceTest()
        {
            using (var impl = new VoweiContextImpl())
            {
                var context = new VoweiContext(impl);
                SerializeDatabaseSingleInstanceTestHelper<Project>(context);
                SerializeDatabaseSingleInstanceTestHelper<Milestone>(context);
                SerializeDatabaseSingleInstanceTestHelper<Ticket>(context);
                SerializeDatabaseSingleInstanceTestHelper<Task>(context);
                SerializeDatabaseSingleInstanceTestHelper<Bug>(context);
                SerializeDatabaseSingleInstanceTestHelper<Requirement>(context);
            }
        }

        [TestMethod]
        public void DeserializeTicketTest()
        {
            var expect = new Ticket()
            {
                Title = "DeserializeTicketTest_Ticket",
                AssignedTo = Setting.DefaultUserName,
                Description = "这是一个测试",
                LastModifiedBy = Setting.DefaultUserName,
                OpenDate = DateTime.Now,
                ClosedDate = DateTime.Now + TimeSpan.FromDays(30),
                Reporter = Setting.DefaultUserName,                
                TagText = "测试"
            };

            var json = JsonConvert.SerializeObject(expect);
            var actual = JsonConvert.DeserializeObject<Ticket>(json);

            Assert.IsNotNull(actual);
            Assert.IsTrue(actual.ID.CompareTo(expect.ID) == 0);
            Assert.IsNull(actual.Reporter);
            Assert.AreEqual(expect.Title, actual.Title);
            Assert.AreEqual(expect.AssignedTo, actual.AssignedTo);
            Assert.AreEqual(expect.LastModifiedBy, actual.LastModifiedBy);

            AreDateTimeEquals(DateTime.Now.Date, actual.LastModified.Date);
            AreDateTimeEquals(expect.OpenDate, actual.OpenDate);
            AreDateTimeEquals(expect.ClosedDate, actual.ClosedDate);
        }

        [TestMethod]
        public void DeserializeMilestoneBasicTest()
        {
            var expect = new Milestone()
            {
                Title = "DeserializeMilestoneTest_Ticket",
                StartDate = DateTime.Now,
                DueDate = DateTime.Now + TimeSpan.FromDays(30),
                OpenDate = DateTime.Now,
                ClosedDate = DateTime.Now + TimeSpan.FromDays(30),
                Reporter = Setting.DefaultUserName
            };

            var json = JsonConvert.SerializeObject(expect);
            var actual = JsonConvert.DeserializeObject<Milestone>(json);

            Assert.IsNotNull(actual);
            Assert.IsTrue(actual.ID.CompareTo(expect.ID) == 0);
            Assert.AreEqual(expect.Title, actual.Title);
            AreDateTimeEquals(expect.StartDate, actual.StartDate);
            AreDateTimeEquals(expect.DueDate, actual.DueDate);
            AreDateTimeEquals(expect.OpenDate, actual.OpenDate);
            AreDateTimeEquals(expect.ClosedDate, actual.ClosedDate);

            Assert.IsNull(actual.Reporter);
        }

        [TestMethod]
        public void DeserializeMilestoneTest()
        {
            var expect = new Milestone()
            {
                Title = "DeserializeMilestoneTest_Milestone",
                StartDate = DateTime.Now,
                DueDate = DateTime.Now + TimeSpan.FromDays(30),
                OpenDate = DateTime.Now,
                ClosedDate = DateTime.Now + TimeSpan.FromDays(30),
                Reporter = Setting.DefaultUserName
            };

            expect.BelongsToMilestone = new Milestone()
            {
                Title = "DeserializeMilestoneTest_Parent"
            };
            var json = JsonConvert.SerializeObject(expect);
            var actual = JsonConvert.DeserializeObject<Milestone>(json);
            Assert.IsNotNull(actual);
            Assert.IsTrue(actual.ID.CompareTo(expect.ID) == 0);
            Assert.AreEqual(expect.Title, actual.Title);

            expect.Children.Add(new Milestone()
            {
                Title = "DeserializeMilestoneTest_Child"
            });
            json = JsonConvert.SerializeObject(expect);
            actual = JsonConvert.DeserializeObject<Milestone>(json);
            Assert.IsNotNull(actual);
            Assert.AreEqual(0, actual.Children.Count());
        }

        [TestMethod]
        public void SerializeMilestoneInJsonAndDeserializeInDotNetTest()
        {
            var expect = new Milestone()
            {
                Title = "SerializeMilestoneInJsonAndDeserializeInDotNetTest_Milestone",
                StartDate = DateTime.Now,
                DueDate = DateTime.Now + TimeSpan.FromDays(30),
                OpenDate = DateTime.Now,
                ClosedDate = DateTime.Now + TimeSpan.FromDays(30),
                Reporter = Setting.DefaultUserName
            };

            expect.BelongsToMilestone = new Milestone()
            {
                Title = "SerializeMilestoneInJsonAndDeserializeInDotNetTest_Parent"
            };
            var json = JsonConvert.SerializeObject(expect);
            var actual = DeserializeHelper<Milestone>(json);
            Assert.IsNotNull(actual);
            Assert.IsTrue(actual.ID.CompareTo(expect.ID) == 0);
            Assert.AreEqual(expect.Title, actual.Title);

            expect.Children.Add(new Milestone()
            {
                Title = "SerializeMilestoneInJsonAndDeserializeInDotNetTest_Child"
            });
            json = JsonConvert.SerializeObject(expect);
            actual = DeserializeHelper<Milestone>(json);
            Assert.IsNotNull(actual);
            Assert.IsNull(actual.Children);
            // Assert.AreEqual(0, actual.Children.Count());
        }

        [TestMethod]
        public void SerializeRequirementInJsonAndDeserializeInDotNetTest()
        {
            var expectedMilestone = new Milestone()
            {
                Title = "SerializeMilestoneInJsonAndDeserializeInDotNetTest_Milestone",
                StartDate = DateTime.Now,
                DueDate = DateTime.Now + TimeSpan.FromDays(30),
                OpenDate = DateTime.Now,
                ClosedDate = DateTime.Now + TimeSpan.FromDays(30),
                Reporter = Setting.DefaultUserName
            };
            var expected = new Requirement()
            {
                Title = "SerializeRequirementInJsonAndDeserializeInDotNetTest_Requirement",
                AssignedTo = Setting.DefaultUserName,
                Description = "这是一个测试",
                LastModifiedBy = Setting.DefaultUserName,
                OpenDate = DateTime.Now,
                ClosedDate = DateTime.Now + TimeSpan.FromDays(30),
                Reporter = Setting.DefaultUserName,
                TagText = "测试",
                BusinessValue = 12345,
                BelongsToMilestone = expectedMilestone
            };
            var json = JsonConvert.SerializeObject(expected);
            var actual = DeserializeHelper<Requirement>(json);

            Assert.IsNotNull(actual);
            Assert.IsTrue(actual.ID.CompareTo(expected.ID) == 0);
            Assert.AreEqual(expected.Title, actual.Title);
            Assert.AreEqual(expected.AssignedTo, actual.AssignedTo);
            Assert.AreEqual(expected.LastModifiedBy, actual.LastModifiedBy);
            Assert.AreEqual(expected.BusinessValue, actual.BusinessValue);
            Assert.AreEqual(expected.TagText, actual.TagText);

            AreDateTimeEquals(expected.OpenDate, actual.OpenDate);
            AreDateTimeEquals(DateTime.MinValue, actual.LastModified);
            AreDateTimeEquals(expected.ClosedDate, actual.ClosedDate);

            Assert.IsTrue(expectedMilestone.ID.CompareTo(actual.BelongsToMilestone.ID) == 0);

            expected.Children.Add(new Requirement()
            {
                Title = "SerializeRequirementInJsonAndDeserializeInDotNetTest_Sub_Requirement",
                BelongsToMilestone = expectedMilestone
            });
            json = JsonConvert.SerializeObject(expected);
            actual = DeserializeHelper<Requirement>(json);

            Assert.AreEqual(1, expected.Children.Count());
        }
                
        [TestMethod]
        public void SerializeTaskInJsonAndDeserializeInDotNetTest()
        {
            var expected = new Task()
            {
                Title = "SerializeTaskInJsonAndDeserializeInDotNetTest_Task",
                AssignedTo = Setting.DefaultUserName,
                Description = "这是一个测试",
                LastModifiedBy = Setting.DefaultUserName,
                OpenDate = DateTime.Now,
                ClosedDate = DateTime.Now + TimeSpan.FromDays(30),
                Reporter = Setting.DefaultUserName,
                TagText = "测试",
                StartDate = DateTime.Now,
                DueDate = DateTime.Now + TimeSpan.FromDays(30),
                Priority = "High",
                Progress = 0.5f,
                LastModified = DateTime.Now,
                Status = "In Progress"
            };
            var json = JsonConvert.SerializeObject(expected);
            var actual = DeserializeHelper<Task>(json);

            Assert.IsNotNull(actual);
            Assert.IsTrue(actual.ID.CompareTo(expected.ID) == 0);
            Assert.AreEqual(expected.Title, actual.Title);
            Assert.AreEqual(expected.AssignedTo, actual.AssignedTo);
            Assert.AreEqual(expected.LastModifiedBy, actual.LastModifiedBy);
            Assert.AreEqual(expected.TagText, actual.TagText);
            Assert.AreEqual(expected.Priority, actual.Priority);
            Assert.AreEqual(expected.Progress, actual.Progress);
            Assert.AreEqual(expected.Status, actual.Status);

            AreDateTimeEquals(expected.OpenDate, actual.OpenDate);
            AreDateTimeEquals(DateTime.MinValue, actual.LastModified);
            AreDateTimeEquals(expected.ClosedDate, actual.ClosedDate);
            AreDateTimeEquals(expected.StartDate, actual.StartDate);
            AreDateTimeEquals(expected.DueDate, actual.DueDate);
        }

        [TestMethod]
        public void SerializeInJsonAndDeserializeInDotNetTicketTest()
        {
            var expect = new Ticket()
            {
                Title = "SerializeInJsonAndDeserializeInDotNetTicketTest_Ticket",
                AssignedTo = Setting.DefaultUserName,
                Description = "这是一个测试",
                LastModifiedBy = Setting.DefaultUserName,
                OpenDate = DateTime.Now,
                ClosedDate = DateTime.Now + TimeSpan.FromDays(30),
                Reporter = Setting.DefaultUserName,
                TagText = "测试"
            };

            var json = JsonConvert.SerializeObject(expect);
            var actual = DeserializeHelper<Ticket>(json);

            Assert.IsNotNull(actual);
            Assert.IsTrue(actual.ID.CompareTo(expect.ID) == 0);
            Assert.AreEqual(expect.Title, actual.Title);
            Assert.AreEqual(expect.AssignedTo, actual.AssignedTo);
            Assert.AreEqual(expect.LastModifiedBy, actual.LastModifiedBy);

            AreDateTimeEquals(expect.OpenDate, actual.OpenDate);
            AreDateTimeEquals(DateTime.MinValue, actual.LastModified);
            AreDateTimeEquals(expect.ClosedDate, actual.ClosedDate);
        }
        
        private T DeserializeHelper<T>(string value)
            where T : class
        {
            var deserializer = new DataContractJsonSerializer(typeof(T));
            using (var stream = new MemoryStream(Encoding.Unicode.GetBytes(value)))
            {
                return deserializer.ReadObject(stream) as T;
            }
        }

        private T JavaScriptSerializerDeserializeHelper<T>(string value)
            where T : class
        {
            var deserializer = new JavaScriptSerializer();
            return deserializer.Deserialize<T>(value);
        }

        [TestMethod]
        public void DeserializeRequirementJsonSerializerTest()
        {
            var json = "{\"Title\":\"DES\\u9879\\u76ee\",\"AssignedTo\":\"leig@vowei.com\",\"BusinessValue\":\"3000\",\"Status\":\"Not Started\",\"Description\":\"测试描述\",\"BelongsToMilestone\":{\"ID\":\"6b9f0b08-1799-489e-a72e-350f8e1e928c\"},\"TagText\":\"\"}";
            var requirement = DeserializeHelper<Requirement>(json);

            Assert.AreEqual("DES项目", requirement.Title);
            Assert.AreEqual("leig@vowei.com", requirement.AssignedTo);
            Assert.AreEqual(3000, requirement.BusinessValue);
            Assert.AreEqual("Not Started", requirement.Status);
            Assert.IsTrue(string.IsNullOrEmpty(requirement.TagText));
            Assert.IsNotNull(requirement.BelongsToMilestone);
            Assert.AreEqual(Guid.Parse("6b9f0b08-1799-489e-a72e-350f8e1e928c"), requirement.BelongsToMilestone.ID);
            Assert.AreEqual("测试描述", requirement.Description);
        }

        [TestMethod]
        public void DeserializeRequirementJavaScriptSerializerTest()
        {
            var json = "{\"Title\":\"DES\u9879\u76ee\",\"AssignedTo\":\"leig@vowei.com\",\"BusinessValue\":\"3000\",\"Status\":\"Not Started\",\"Description\":\"测试描述\",\"BelongsToMilestone\":{\"ID\":\"6b9f0b08-1799-489e-a72e-350f8e1e928c\"},\"TagText\":\"\"}";
            var requirement = JavaScriptSerializerDeserializeHelper<Requirement>(json);

            Assert.AreEqual("DES项目", requirement.Title);
            Assert.AreEqual("leig@vowei.com", requirement.AssignedTo);
            Assert.AreEqual(3000, requirement.BusinessValue);
            Assert.AreEqual("Not Started", requirement.Status);
            Assert.IsTrue(string.IsNullOrEmpty(requirement.TagText));
            Assert.IsNotNull(requirement.BelongsToMilestone);
            Assert.AreEqual(Guid.Parse("6b9f0b08-1799-489e-a72e-350f8e1e928c"), requirement.BelongsToMilestone.ID);
            Assert.AreEqual("测试描述", requirement.Description);
        }
    }
}
